'''
Author: caishuyang
Date: 2023-02-20 17:30:29
LastEditors: caishuyang
LastEditTime: 2023-03-04 10:37:51
Description: excel读取器，同时提供归一化和反归一化函数
'''

import pandas as pd
import numpy as np
import warnings
import torch
import sys


class XlsReader():
    def __init__(self, feature_num, label_num, path='Data\\Data.xlsx',
                 sheet_name='Sheet1', header=None, normalmode="z-score"):
        '''
            normalmode 目前提供模式: z-score, m-scaler
        '''
        self.feature_num = feature_num
        self.label_num = label_num

        self.xls = pd.read_excel(path, sheet_name, header=header)
        self.data = self.xls.values

        self.features = self.data[:, 0:feature_num]
        self.features = np.array(self.features)
        self.labels = self.data[:, feature_num:feature_num+label_num]
        self.labels = np.array(self.labels)

        self.mmFeature, self.mmLabel = self.__getMinMax__()
        self.msFeature, self.msLabel = self.__getMeanStd__()

        self.normalFeature = None
        '''
            归一化特征数据函数
        '''
        self.normalLabel = None
        '''
            归一化标签数据函数
        '''
        self.denormalTarget = None
        '''
            反归一化目标数据函数
        '''
        self.__initNormal__(normalmode)

        self.features = self.normalFeature(self.features)
        #self.labels = self.normalLabel(self.labels)
        self.features = torch.Tensor(self.features)
        self.labels = torch.Tensor(self.labels)
        self.groupnum = len(self.data)

    def __initNormal__(self, mode):
        '''
            初始化归一化和反归一化函数
        '''
        if mode == "z-score":
            self.normalFeature = self.__z_scoreFeature__
            self.normalLabel = self.__z_scoreLabel__
            self.denormalTarget = self.__dez_scoreTarg__
        elif mode == "m-scaler":
            self.normalFeature = self.__scalerFeature__
            self.normalLabel = self.__scalerLabel__
            self.denormalTarget = self.__descalerTarg__
        else:
            print("请使用以下归一化模式之一:\nz-score,m-scaler")
            sys.exit()

    def __getMinMax__(self):
        '''
            MinMaxScaler归一化准备,得到min和max-min
        '''
        mmFestures = []
        mmLabels = []
        for i in range(self.feature_num):
            min_ = np.min(self.features[:, i])
            max_ = np.max(self.features[:, i])
            mmFestures.append((min_, max_-min_))
        for i in range(self.label_num):
            min_ = np.min(self.labels[:, i])
            max_ = np.max(self.labels[:, i])
            mmLabels.append((min_, max_-min_))
        return mmFestures, mmLabels

    def __getMeanStd__(self):
        '''
            z-score归一化准备,得到mean和sigma
        '''
        msFeatures = []
        msLabels = []
        for i in range(self.feature_num):
            mean = np.mean(self.features[:, i])
            std = np.std(self.features[:, i])
            msFeatures.append((mean, std))
        for i in range(self.label_num):
            mean = np.mean(self.labels[:, i])
            std = np.std(self.labels[:, i])
            msLabels.append((mean, std))
        return msFeatures, msLabels

    # 归一化与反归一化，这一块可以改成矩阵运算提高执行效率，但因为数据量小，也没必要

    # MinMaxScaler 模式
    def __scalerFeature__(self, features):
        '''
            MinMaxScaler 归一化特征数据
        '''
        for i in range(self.feature_num):
            min_ = self.mmFeature[i][0]
            max_min = self.mmFeature[i][1]
            try:
                features[:, i] = (features[:, i]-min_)/max_min
            except:
                warnings.warn("用于训练的特征数据存在问题", Warning)
                sys.exit()
        return features

    def __scalerLabel__(self, labels):
        '''
            MinMaxScaler 归一化标签数据
        '''
        for i in range(self.label_num):
            min_ = self.mmLabel[i][0]
            max_min = self.mmLabel[i][1]
            try:
                labels[:, i] = (labels[:, i]-min_)/max_min
            except:
                warnings.warn("用于训练的标签数据存在问题", Warning)
                sys.exit()
        return labels

    def __descalerTarg__(self, targets):
        '''
            MinMaxScaler 反归一化目标数据
        '''
        for i in range(self.label_num):
            min_ = self.mmLabel[i][0]
            max_min = self.mmLabel[i][1]
            targets[:, i] = max_min*targets[:, i]+min_
        return targets

    # z-score模式
    def __z_scoreFeature__(self, features):
        '''
            z-score 归一化特征数据
        '''
        for i in range(self.feature_num):
            mean = self.msFeature[i][0]
            std = self.msFeature[i][1]
            try:
                features[:, i] = (features[:, i]-mean)/std
            except:
                warnings.warn("用于训练的特征数据存在问题", Warning)
                sys.exit()
        return features

    def __z_scoreLabel__(self, labels):
        '''
            z-score 归一化标签数据
        '''
        for i in range(self.label_num):
            mean = self.msLabel[i][0]
            std = self.msLabel[i][1]
            try:
                labels[:, i] = (labels[:, i]-mean)/std
            except:
                warnings.warn("用于训练的标签数据存在问题", Warning)
                sys.exit()
        return labels

    def __dez_scoreTarg__(self, targets):
        '''
            z-score 反归一化目标数据
        '''
        for i in range(self.label_num):
            mean = self.msLabel[i][0]
            std = self.msLabel[i][1]
            targets[:, i] = std*targets[:, i]+mean
        return targets


if __name__ == '__main__':
    data = XlsReader(2, 2, normalmode="m-scaler")
    print(data.groupnum)
